package org.orbis.index;

import java.io.IOException;
import org.msgpack.MessagePackObject;
import org.msgpack.MessageTypeException;
import org.msgpack.object.ArrayType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.NilType;
import org.msgpack.object.RawType;
import org.msgpack.rpc.Request;
import org.msgpack.rpc.Server;
import org.msgpack.rpc.loop.EventLoop;
import org.orbis.communicate.MainService;
import org.orbis.communicate.deleter.DeleterPackage;
import org.orbis.constants.Constants;
import org.orbis.logging.LogParser;
import org.orbis.structure.ContentInfo;

/** It works as server to index data, data structures are specified by
 * setting file or other node's setting on startup.
 * 
 * @author fujisaka_yusuke */
public class IndexService{
	public boolean stopFlag;
	private MainService mains;
	private ContentInfo contentInfo;
	private int port;
	private Server server;

	public IndexService(MainService mains, ContentInfo contentInfo, int port){
		this.mains = mains;
		this.contentInfo = contentInfo;
		this.port = port;
	}

	// RPC
	public class IndexRPCHandler{
		// FIXME: If messagepack-rpc improves
		public void index(Request req) throws Exception{
			req.sendResult(NilType.create());
			MessagePackObject obj = req.getArguments();
			MessagePackObject[] args = obj.asArray();

			if(args.length < contentInfo.totalNum()){
				req.sendError(RawType.create("Error(index): Not efficient index word"));
				LogParser.severe("Error(index): Not efficient index word, " + contentInfo.totalNum()
					+ " needs but actually " + args.length + ".");
			}
			IndexingPackage pkg = new IndexingPackage(contentInfo);

			try{
				for(int i = 0; i < contentInfo.contentsNum(); i++){
					pkg.setContent(args[i].asString(), i);
				}
				int index = contentInfo.contentsNum();
				for(int i = 0; i < contentInfo.appendixNum(); i++){
					pkg.setAppendix(args[index + i].asString(), i);
				}
				index += contentInfo.appendixNum();
				for(int i = 0; i < contentInfo.binaryFlagNum(); i++){
					try{
						pkg.setBinaryFlag(args[index + i].asBoolean(), i);
					}catch(MessageTypeException e){
						LogParser.severe("Type error: Boolean supposed but " + args[index + i].toString());
						return;
					}
				}
				index += contentInfo.binaryFlagNum();
				for(int i = 0; i < contentInfo.integerFlagNum(); i++){
					try{
						pkg.setIntFlag(args[index + i].asInt(), i);
					}catch(MessageTypeException e){
						LogParser.severe("Type error: Integer supposed but " + args[index + i].toString());
						return;
					}
				}
			}catch(Exception e){
				e.printStackTrace();
			}

			mains.postIndex(pkg);
		}

		public void delete(Request req){
			req.sendResult(NilType.create());
			MessagePackObject obj = req.getArguments();
			MessagePackObject[] args = obj.asArray();

			if(args.length < 2 || !args[0].isIntegerType()){
				req.sendError(RawType.create("Error(delete): Input delete key"));
			}

			switch(args[0].asByte()){
			case Constants.DELETE_TYPE_ID:
				mains.postDelete(new DeleterPackage(args[1].asLong()));
				break;
			case Constants.DELETE_TYPE_CONTENT:
				mains.postDelete(new DeleterPackage(true, args[1].asString()));
				break;
			case Constants.DELETE_TYPE_APPENDIX:
				mains.postDelete(new DeleterPackage(false, args[1].asString()));
				break;
			case Constants.DELETE_TYPE_INTFLAG:
				if(args.length < 3) break;
				mains.postDelete(new DeleterPackage(args[1].asInt(), args[2].asInt()));
				break;
			case Constants.DELETE_TYPE_LONGFLAG:
				if(args.length < 3) break;
				mains.postDelete(new DeleterPackage(args[1].asInt(), args[2].asInt()));
				break;
			default:
				break;
			}
		}

		public void argument(Request req){
			MessagePackObject[] args = new MessagePackObject[4];

			args[0] = IntegerType.create(contentInfo.contentsNum());
			args[1] = IntegerType.create(contentInfo.appendixNum());
			args[2] = IntegerType.create(contentInfo.binaryFlagNum());
			args[3] = IntegerType.create(contentInfo.integerFlagNum());

			req.sendResult(ArrayType.create(args));
		}

		public void read(Request req){
			req.sendResult(NilType.create());
			mains.readMemory();
		}

		public void save(Request req){
			req.sendResult(NilType.create());
			mains.saveMemory();
		}

		public void suspend(Request req){
			req.sendResult(NilType.create());
			// FIXME: to call suspend function
			mains.suspend(null, port, 0);
		}

		public void stop(Request req){
			req.sendResult(NilType.create());
			mains.terminate();
			this.notifyAll();
		}
	}

	public void initService(){
		EventLoop eventLoop = EventLoop.defaultEventLoop();
		try{
			server = new Server(eventLoop);
			server.serve(new IndexRPCHandler());
			server.listen(port);
		}catch(IOException e){}
	}

	public void stopService(){
		server.close();
	}

	@Override
	protected void finalize() throws Throwable{
		if(server != null) server.close();
		super.finalize();
	}
}
